package ru.amse.stroganova.ui.action;

import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.filechooser.FileFilter;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.xml.sax.SAXException;

import ru.amse.stroganova.MainWindow;
import ru.amse.stroganova.io.GraphLoader;
import ru.amse.stroganova.io.GraphSaver;
import ru.amse.stroganova.presentation.GraphPresentation;
import ru.amse.stroganova.ui.GraphComponent;
import ru.amse.stroganova.ui.NewGraphDialog;

/**
 * Presents a factory for main frame file actions.
 * 
 * @author Dasha Stroganova
 * 
 */
public class FileActionsFactory {

	private final JFrame mainFrame;
	
	private final GraphComponent graphComponent;
	
	private File currentFile;
	
	private final Action newAction;
	
	private final Action openAction;
	
	private final Action saveAction;
	
	private final Action saveAsAction;
	
	private final Action exitAction;
	
	/**
	 * Creates new file actions factory for given graph component and main frame.
	 * 
	 * @param mainWindow
	 * @param graphComponent
	 */
	public FileActionsFactory(MainWindow mainWindow, GraphComponent graphComponent) {
		this.mainFrame = mainWindow;
		this.graphComponent = graphComponent;
		
		newAction = new NewAction();
		openAction = new OpenAction();
		saveAction = new SaveAction();
		saveAsAction = new SaveAsAction();
		exitAction = new ExitAction();
	}
	
	/**
	 * Returns an action for exit.
	 * 
	 * @return action
	 */
	public Action getExitAction() {
		return exitAction;
	}
	
	/**
	 * Returns an action for new graph.
	 * 
	 * @return action
	 */
	public Action getNewAction() {
		return newAction;
	}
	
	/**
	 * Returns an action for open file.
	 * 
	 * @return action
	 */
	public Action getOpenAction() {
		return openAction;
	}
	
	/**
	 * Returns an action for saving.
	 * 
	 * @return action
	 */
	public Action getSaveAction() {
		return saveAction;
	}
	
	/**
	 * Returns an action for saving as.
	 * 
	 * @return action
	 */
	public Action getSaveAsAction() {
		return saveAsAction;
	}
	
	private boolean isSavingCancelled() {
		if (graphComponent.wasChanged()) {
			int choice = JOptionPane.showOptionDialog(mainFrame, "Graph has changed.\nDo you want to save the changes?", "Save?", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null);
			if (choice == JOptionPane.YES_OPTION) {
				if (saveAction.isEnabled()) {
					saveAction.actionPerformed(null);
				} else {
					saveAsAction.actionPerformed(null);
				}
			} else if (choice == JOptionPane.CANCEL_OPTION) {
				return false;
			}
		}
		return true;
	}
	
	private class ExitAction extends AbstractAction {

		/* package */ExitAction() {
			super("Exit");
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_X,
					InputEvent.CTRL_MASK));
		}

		public void actionPerformed(ActionEvent e) {
			if (!isSavingCancelled()) {
				return;
			}
			System.exit(0);
		}
	}

	private class NewAction extends AbstractAction {

		/* package */NewAction() {
			super("New");
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_N,
					InputEvent.CTRL_MASK));
			putValue(SMALL_ICON, new ImageIcon(ClassLoader.getSystemResource("img/new.gif")));
		}

		public void actionPerformed(ActionEvent e) {
			if (!isSavingCancelled()) {
				return;
			}
			final NewGraphDialog newGraphDialog = new NewGraphDialog(mainFrame);
			newGraphDialog.addWindowListener(new WindowAdapter() {
				@Override
				public void windowClosed(WindowEvent event) {
					
					if (newGraphDialog.wasTypeChoosen()) {
						GraphPresentation presentation;
						presentation = new GraphPresentation(newGraphDialog.isDirected(), newGraphDialog.isWeighted());
						graphComponent.setGraphPresentation(presentation);
						saveAction.setEnabled(false);
						saveAsAction.setEnabled(true);
						currentFile = null;
					}
				}	
			});
		}
	}
	
	private class OpenAction extends AbstractAction {
		
		private JFileChooser fc = new JFileChooser();
		
		/* package */OpenAction() {
			super("Open..");
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O,
					InputEvent.CTRL_MASK));
			putValue(SMALL_ICON, new ImageIcon(ClassLoader.getSystemResource("img/open.gif")));
		}

		public void actionPerformed(ActionEvent e) {
			if (!isSavingCancelled()) {
				return;
			}
			File file;
			if (showFileChooser() == JFileChooser.APPROVE_OPTION) {
				file = fc.getSelectedFile();
				GraphLoader parser = new GraphLoader(file);
				try {
					parser.parsePresentation();
				} catch (ParserConfigurationException e1) {
					JOptionPane.showMessageDialog(mainFrame, "Can't open file: \n\n" + e1.getMessage(), "Error opening file", JOptionPane.ERROR_MESSAGE);
					return;
				} catch (SAXException e1) {
					StringBuilder messages = new StringBuilder();
					if (e1.getMessage() != null) {
						messages.append(e1.getMessage());
						messages.append("\n");
					}
					for (int i = 0; i < parser.getErrorMessages().size(); i++) {
						messages.append(parser.getErrorMessages().get(i));
						messages.append("\n");
					}
					JOptionPane.showMessageDialog(mainFrame, "Invalid file: \n\n" + messages, "Error opening file", JOptionPane.ERROR_MESSAGE);
					return;
				} catch (IOException e1) {
					JOptionPane.showMessageDialog(mainFrame, "Can't open file: \n\n" + e1.getMessage(), "Error opening file", JOptionPane.ERROR_MESSAGE);
					return;
				}
				
				saveAction.setEnabled(true);
				currentFile = file;
				
				graphComponent.setGraphPresentation(parser.getGraphPresentation());
				graphComponent.setWasChanged(false);
				saveAsAction.setEnabled(true);
			}
		}
		
		private int showFileChooser() {
			fc.setDialogTitle("Open file");
			fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
			fc.setAcceptAllFileFilterUsed(false);
			fc.addChoosableFileFilter(new FileFilter() {

				@Override
				public boolean accept(File file) {
					return file.isDirectory() || file.getName().endsWith(".xml");
				}

				@Override
				public String getDescription() {
					return "XML document(*.xml)";
				}
			});
			
			return fc.showOpenDialog(mainFrame);
		}
	}
	
	private class SaveAction extends AbstractAction {
		
		/* package */SaveAction() {
			super("Save");
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S,
					InputEvent.CTRL_MASK));
			putValue(SMALL_ICON, new ImageIcon(ClassLoader.getSystemResource("img/save.gif")));
			setEnabled(false);
		}
		
		public void actionPerformed(ActionEvent e) {
			try {
				(new GraphSaver(currentFile)).saveGraphPresentation(graphComponent.getGraphPresentation());
			} catch (ParserConfigurationException e1) {
				JOptionPane.showMessageDialog(mainFrame, "Can't save file: \n\n" + e1.getMessage(), "Error saving file", JOptionPane.ERROR_MESSAGE);
				return;
			} catch (TransformerException e1) {
				JOptionPane.showMessageDialog(mainFrame, "Can't save file: \n\n" + e1.getMessage(), "Error saving file", JOptionPane.ERROR_MESSAGE);
				return;
			}
			graphComponent.setWasChanged(false);
		}
		
	}
	
	private class SaveAsAction extends AbstractAction {
		
		private JFileChooser fc = new JFileChooser();
		
		/* package */SaveAsAction() {
			super("Save As..");
			putValue(SMALL_ICON, new ImageIcon(ClassLoader.getSystemResource("img/saveAs.gif")));
			setEnabled(false);
		}
		
		public void actionPerformed(ActionEvent e) {
			File file;
			if (showFileChooser() == JFileChooser.APPROVE_OPTION) {
				file = fc.getSelectedFile();
				if (!file.getName().endsWith(".xml")) {
					file = new File(file.getAbsolutePath() + ".xml");
				}
				if (file.exists()) {
					if (JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(mainFrame, file.getAbsoluteFile() + " already exists.\nDo you want to replace it?", "Save Graph", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE)) {
						return;
					}
				}
				try {
					(new GraphSaver(file)).saveGraphPresentation(graphComponent.getGraphPresentation());
				} catch (ParserConfigurationException e1) {
					JOptionPane.showMessageDialog(mainFrame, "Can't save file: \n\n" + e1.getMessage(), "Error saving file", JOptionPane.ERROR_MESSAGE);
					return;
				} catch (TransformerException e1) {
					JOptionPane.showMessageDialog(mainFrame, "Can't save file: \n\n" + e1.getMessage(), "Error saving file", JOptionPane.ERROR_MESSAGE);
					return;
				}
				saveAction.setEnabled(true);
				graphComponent.setWasChanged(false);
				currentFile = file;
			}
		}
		
		private int showFileChooser() {
			fc.setDialogTitle("Save file");
			fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
			fc.setAcceptAllFileFilterUsed(false);
			fc.addChoosableFileFilter(new FileFilter() {

				@Override
				public boolean accept(File file) {
					return file.isDirectory() || file.getName().endsWith(".xml");
				}

				@Override
				public String getDescription() {
					return "XML document(*.xml)";
				}
			});
			
			return fc.showSaveDialog(mainFrame);
		}
	}
}
